#include <iostream>
#include <stdlib.h>
#include <string>
#include <vector>
#include <ctime>
#include <cassert>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

using namespace std;

#define MakeSeed() srand((unsigned long)time(nullptr) ^ getpid() ^ 0x142536 ^ rand() % 1243)
// 这些数字没有别的含义，只是为了让数字更加随机

#define PROCESS_NUM 5

/////////////////////////////////////////////子进程要完成的某种任务 ---- 模拟一下////////////////////////////////////////
// 函数指针
typedef void (*func_t)();

void downLoadTask()
{
    cout << getpid() << ": 下载任务\n"
         << endl;
    sleep(1);
}

void ioTask()
{
    cout << getpid() << ": IO任务\n"
         << endl;
    sleep(1);
}

void flushTask()
{
    cout << getpid() << ": 刷新任务\n"
         << endl;
    sleep(1);
}

void loadTaskFunc(vector<func_t> *out)
{
    assert(out);
    out->push_back(downLoadTask);
    out->push_back(ioTask);
    out->push_back(flushTask);
}

/////////////////////////////////////////////////////下面的代码是一个多进程程序//////////////////////////////
// 封装一个类
class subEp // endpoint 管道另一端
{
public:
    // subEp(const string &name, pid_t subId,int writeFd)
    // :name_(name)
    // ,subId_(subId)
    // ,writeFd_(writeFd)
    // {

    // }

    // 方便取名字,所以在构造的时候就不用name_
    subEp(pid_t subId, int writeFd)
        : subId_(subId), writeFd_(writeFd)
    {
        char nameBuffer[1024];
        snprintf(nameBuffer, sizeof nameBuffer, "process-%d [pid(%d) - fd(%d)]", num++, subId_, writeFd_);
        name_ = nameBuffer;
    }

public:
    static int num;
    string name_;
    pid_t subId_;
    int writeFd_;
};

int subEp::num = 0;

int recvTask(int readFd)
{
    int code = 0;
    ssize_t s = read(readFd, &code, sizeof code);
    // assert(s == sizeof(int)); 不能简单断言
    if (s == 4)
        return code;
    else if (s <= 4)
        return -1;
    else
        return 0;
}

void sendTask(const subEp &process, int taskNum)
{
    cout << "send task num: " << taskNum << "  send to -> " << process.name_ << endl;
    int n = write(process.writeFd_, &taskNum, sizeof(taskNum));
    assert(n == sizeof(int));
    (void)n;
}

void creatSubProcess(vector<subEp> *subs, vector<func_t> &funcMap)
{
    for (int i = 0; i < PROCESS_NUM; i++)
    {
        int fds[2];
        int n = pipe(fds);
        assert(n == 0); // 保证建立管道通信成功
        (void)n;        //

        pid_t id = fork();
        if (id == 0)
        {
            // 子进程，进行处理任务
            close(fds[1]); // 关闭子进程写端
            while (true)
            {
                // 1.获取命令码,如果没有发送，我们子进程应该阻塞
                int commandCode = recvTask(fds[0]);
                // 2.完成任务
                if (commandCode >= 0 && commandCode < funcMap.size())
                    funcMap[commandCode]();
                else if (commandCode == -1) // 退出码有问题，不在运行子进程，直接退出
                    // cout << " sub recv code error!" << endl;
                    break;
            }
            exit(0);
        }
        // 下面是父进程
        close(fds[0]); // 关闭子进程读端

        subEp sub(id, fds[1]); // 构造

        // subs.push_back(sub);//vector
        subs->push_back(sub); // 必须使用->,因为是指针
    }
}

void loadTaskBlance(const vector<subEp> &subs, const vector<func_t> &funcMap, int count)
{
    int processnum = subs.size();
    int tasknum = funcMap.size();
    // bool quit = false;
    bool forever = (count == 0 ? true : false);

    while (true)
    {
        // 1. 选择一个子进程 --> vector<subEp> -> index  -- 随机数
        int subIdx = rand() % processnum;
        // 2. 选择一个任务  --> vector<func_t> -> index
        int taskIdx = rand() % tasknum;
        // 3. 任务发送给选择的进程
        sendTask(subs[subIdx], taskIdx);
        sleep(1);

        if (!forever)
        {
            count--;
            if (count == 0)
                break;
        }
    }

    // write quit -> read 0 //写端关闭，读到0文件结尾
    for (int i = 0; i < processnum; i++)
        close(subs[i].writeFd_);
}

void waitProcess(vector<subEp> &processes)
{
    int processnum = processes.size();
    for (int i = 0; i < processnum; i++)
    {
        waitpid(processes[i].writeFd_, nullptr, 0);
        cout << "wait sub process success ..." << processes[i].subId_ << endl;
    }
}

int main()
{
    MakeSeed();
    // 1.建立子进程并建立和子进程通信的信道，有bug的，但是不影响我们后面编写
    // [子进程id,wfd]
    // 1.1 加载方法表
    // 定义一个方法表，将所有的任务放进去
    vector<func_t> funcMap;
    loadTaskFunc(&funcMap);

    // 1.2 创建子进程，并且维护好父子通信信道
    vector<subEp> subs;
    creatSubProcess(&subs, funcMap); // funcMap说明有哪些方法可以使用

    // 2.走到这里就是父进程，控制子进程，负载均衡的向子进程发送命令码

    // 可以将父进程代码封装一下
    int taskCnt = 5; // 如果是0，则永远运行
    loadTaskBlance(subs, funcMap, taskCnt);
    // int processnum = subs.size();
    // int tasknum = funcMap.size();
    // bool quit = false;
    // while(!quit)
    // {
    //     // 1. 选择一个子进程 --> vector<subEp> -> index  -- 随机数
    //     int subIdx = rand() % processnum;
    //     // 2. 选择一个任务  --> vector<func_t> -> index
    //     int taskIdx = rand() % tasknum;
    //     // 3. 任务发送给选择的进程
    //     sendTask( subs[subIdx], taskIdx);
    //     sleep(1);
    // }

    // 3.回收子进程信息
    waitProcess(subs);
    return 0;
}